package com.joe.core;

import com.joe.annotations.*;
import com.joe.entity.exceptions.AnnotationNotFoundException;
import com.joe.entity.exceptions.NoSqlException;
import com.joe.entity.exceptions.TypeNotMatchException;
import com.joe.entity.tuples.TwoTuple;
import com.joe.utils.DBUtils;
import com.joe.utils.ReflectUtils;
import com.joe.utils.StringUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by joe on 2018/11/25
 */
public class ProxyHandler implements InvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        List<Object> resultList = new ArrayList<>();
        Annotation[] annotations = method.getAnnotations();

        if (annotations.length == 0) {
            throw new AnnotationNotFoundException("Can not find usable annotation from method -> " + method.getName());
        }

        for (Annotation annotation : annotations) {
            String orgSql = readPreSql(annotation);

            if (StringUtils.isEmpty(orgSql)) {
                throw new NoSqlException("Can not find preSql from annotation -> "
                        + annotation.getClass().getName() + " of method -> " + method.getName());
            }
            TwoTuple<String, Object[]> tuple = SqlMapper.getInstance().doMapping(orgSql, args);
            resultList.add(execute(method, annotation, tuple.first, tuple.second));
        }

        if (resultList.size() == 1) {
            return resultList.get(0);
        }

        return resultList.toArray();
    }


    private String readPreSql(Annotation annotation) {
        String value;
        String simpleName = annotation.annotationType().getSimpleName();
        switch (simpleName) {
            case "Insert":
                value = ((Insert) annotation).value();
                break;
            case "Delete":
                value = ((Delete) annotation).value();
                break;
            case "Drop":
                value = ((Drop) annotation).value();
                break;
            case "Update":
                value = ((Update) annotation).value();
                break;
            case "Alter":
                value = ((Alter) annotation).value();
                break;
            case "Select":
                value = ((Select) annotation).value();
                break;
            default:
                throw new UnsupportedOperationException("Can not find supported annotation called " + simpleName);
        }
        return value;
    }

    /**
     * 执行sql
     *
     * @param annotation 注解对象
     * @param preSql     预处理sql
     * @param args       预处理参数
     */
    private Object execute(Method method, Annotation annotation, String preSql, Object[] args) throws UnsupportedOperationException, InstantiationException, IllegalAccessException, SQLException, TypeNotMatchException, InvocationTargetException, NoSuchMethodException {
        Object result = null;
        String simpleName = annotation.annotationType().getSimpleName();

        switch (simpleName) {
            case "Insert":
                return DBUtils.insert(preSql, args);
            case "Delete":
                return DBUtils.delete(preSql, args);
            case "Drop":
                break;
            case "Update":
                return DBUtils.update(preSql, args);
            case "Alter":
                break;
            case "Select":
                ResultMapper resultMapper = new ResultMapper();  // TODO 看看后期能不能转为单例模式
                ResultSet resultSet = DBUtils.select(preSql, args);
                result = resultMapper.doMapping(ReflectUtils.getReturnType(method), resultSet);
                resultSet.close();
                break;
            default:
                throw new UnsupportedOperationException("");
        }
        return result;
    }


}
